/*******************************************************************************
  Copyright(c) 2009 Geoffrey Hausheer. All rights reserved.

  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the Free
  Software Foundation; either version 2 of the License, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  more details.

  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc., 59
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.

  The full GNU General Public License is included in this distribution in the
  file called LICENSE.

  Contact Information: dso-tonight-at-phracturedblue-dot-com
*******************************************************************************/

package dsotonight;

import java.awt.Color;
import org.jdesktop.application.Action;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import org.jdesktop.application.TaskMonitor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.lang.String;
import java.net.URL;
import javax.swing.Timer;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JFrame;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.prefs.Preferences;
import javax.imageio.ImageIO;
import javax.swing.JViewport;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;


/**
 * The application's main frame.
 */
public class DSOTonightView extends FrameView {

    public DSOTonightView(SingleFrameApplication app, double _lonW, double _latN) {
        super(app);
        lonW = _lonW;
        latN = _latN;

        initComponents();
        longitudeField.setText(SiderealDate.DeclinationToString(lonW));
        latitudeField.setText(SiderealDate.DeclinationToString(latN));
        image = new SkyDBImage();
        setColumnRenderer();
        ((SkyDBTableModel)jXTable1.getModel()).loadData();
        ((SearchPanel)searchPanel).initSearch(jXTable1, lonW, latN);
        ((ExpandPanel) commentExPanel).attach(commentPanel, false);
        ((ExpandPanel) propExPanel).attach(propPanel, false);


        ListSelectionListener jXTable1Listener;
        jXTable1Listener = new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent e) {
                // If cell selection is enabled, both row and column change events are fired
                if (e.getSource() == jXTable1.getSelectionModel() && jXTable1.getRowSelectionAllowed()) {
                    getRowDetails(jXTable1.getSelectedRow());
                }
            }
        };
        jXTable1.getSelectionModel().addListSelectionListener(jXTable1Listener);
        jXTable1.getColumnModel().getSelectionModel().addListSelectionListener(jXTable1Listener);

        // status bar initialization - message timeout, idle icon and busy animation, etc
        ResourceMap resourceMap = getResourceMap();
        int messageTimeout = resourceMap.getInteger("StatusBar.messageTimeout");
        messageTimer = new Timer(messageTimeout, new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                statusMessageLabel.setText("");
            }
        });
        messageTimer.setRepeats(false);

        timeFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM);
        clockRefresh = new Timer(1000, new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                localTimeField.setText(timeFormat.format(new Date()));
                siderealTimeField.setText(SiderealDate.RightAscensionToString(SiderealDate.SiderealTime(Calendar.getInstance(), lonW)));
            }
        });
        clockRefresh.setRepeats(true);
        clockRefresh.start();

        int busyAnimationRate = resourceMap.getInteger("StatusBar.busyAnimationRate");
        for (int i = 0; i < busyIcons.length; i++) {
            busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons[" + i + "]");
        }
        busyIconTimer = new Timer(busyAnimationRate, new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                busyIconIndex = (busyIconIndex + 1) % busyIcons.length;
                statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);
            }
        });
        idleIcon = resourceMap.getIcon("StatusBar.idleIcon");
        statusAnimationLabel.setIcon(idleIcon);
        progressBar.setVisible(false);

        // connecting action tasks to status bar via TaskMonitor
        TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());
        taskMonitor.addPropertyChangeListener(new java.beans.PropertyChangeListener() {

            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                String propertyName = evt.getPropertyName();
                if ("started".equals(propertyName)) {
                    if (!busyIconTimer.isRunning()) {
                        statusAnimationLabel.setIcon(busyIcons[0]);
                        busyIconIndex = 0;
                        busyIconTimer.start();
                    }
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(true);
                } else if ("done".equals(propertyName)) {
                    busyIconTimer.stop();
                    statusAnimationLabel.setIcon(idleIcon);
                    progressBar.setVisible(false);
                    progressBar.setValue(0);
                } else if ("message".equals(propertyName)) {
                    String text = (String) (evt.getNewValue());
                    statusMessageLabel.setText((text == null) ? "" : text);
                    messageTimer.restart();
                } else if ("progress".equals(propertyName)) {
                    int value = (Integer) (evt.getNewValue());
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(false);
                    progressBar.setValue(value);
                }
            }
        });
    }

    @Action
    public void showAboutBox() {
        if (aboutBox == null) {
            JFrame mainFrame = DSOTonightApp.getApplication().getMainFrame();
            aboutBox = new DSOTonightAboutBox(mainFrame);
            aboutBox.setLocationRelativeTo(mainFrame);
        }
        DSOTonightApp.getApplication().show(aboutBox);
    }

    static class RARenderer extends DefaultTableCellRenderer {

        public RARenderer() {
            super();
        }

        public void setValue(Object value) {
            setText((value == null) ? "" : SiderealDate.RightAscensionToString((Double) value));
        }
    }

    static class DecRenderer extends DefaultTableCellRenderer {

        public DecRenderer() {
            super();
        }

        public void setValue(Object value) {
            setText((value == null) ? "" : SiderealDate.DeclinationToString((Double) value));
        }
    }
    static class TypeRenderer extends DefaultTableCellRenderer {

        public TypeRenderer() {
            super();
        }

        public void setValue(Object value) {
            setText((value == null) ? "" : SkyDBTableModel.typeToString((Integer) value));

        }
    }

    private void setColumnRenderer() {

        // First, the long way to assign--just to show there's a Sorter involved
        jXTable1.getColumnExt("Dec").setCellRenderer(new DecRenderer());
        jXTable1.getColumnExt("RA").setCellRenderer(new RARenderer());
        jXTable1.getColumnExt("Type").setCellRenderer(new TypeRenderer());

    }

    private void getRowDetails(int row) {
        SkyDBTableModel model;
        String addr;
        String Catalog;
        double RA;
        double Dec;

        if (row < 0 || row >= jXTable1.getRowCount())
            return;
        model = (SkyDBTableModel)jXTable1.getModel();
        Catalog = (String)model.getValueAt(jXTable1.convertRowIndexToModel(row), 0);
        RA = (Double)model.getValueAt(jXTable1.convertRowIndexToModel(row), 2);
        Dec = (Double)model.getValueAt(jXTable1.convertRowIndexToModel(row), 3);
        ArrayList<String> names = model.getNames(Catalog);
        images = (Hashtable)image.findImages(names);
        //Make a copy so we don't pollute the original with new stuff
        if(images == null) {
            images = new Hashtable();
        } else {
            images = (Hashtable)images.clone();
        }

        namesArea.setText("");
        for (Iterator i = names.iterator(); i.hasNext();) {
            namesArea.append(i.next() + "\n");
        }
        imageList.removeAllItems();
        imageList.addItem("None");
        for (Enumeration e = images.keys(); e.hasMoreElements();) {
            imageList.addItem(e.nextElement());
        }
        imageList.addItem("DSS");
        imageList.addItem("SDSS");
        addr = "http://archive.stsci.edu/cgi-bin/dss_search?v=1&r=" 
                + String.format("%02d", (int) RA) + "+"
                + String.format("%02d", (int) SiderealDate.Minutes(RA)) + "+"
                + String.format("%02d", (int) SiderealDate.Seconds(RA))
                + "&d=" + (Dec >= 0 ? "+" : "")
                + String.format("%02d", (int) Dec) + "+"
                + String.format("%02d", (int) SiderealDate.Minutes(Dec)) + "+"
                + String.format("%02d", (int) SiderealDate.Seconds(Dec))
                + "&e=J2000&h=15.0&w=15.0&f=gif&c=none&fov=NONE";
        images.put("DSS", addr);
        addr = "http://casjobs.sdss.org/ImgCutoutDR6/getjpeg.aspx?ra="
                + RA / 24 * 360 + "&dec=" + Dec + "&scale=1.0&width=600&height=600&opt=GST&query=SR(10,20)";
        images.put("SDSS", addr);
        images.put("None", "");
        imageList.setSelectedIndex(0);
        //NGC6910
        //http://archive.stsci.edu/cgi-bin/dss_search?v=1&r=20+23+08&d=+40+46+29&e=J2000&h=15.0&w=15.0&f=gif&c=none&fov=NONE
        //http://casjobs.sdss.org/ImgCutoutDR6/getjpeg.aspx?ra=305.783333&dec=40.774722&scale=1.0&width=600&height=600&opt=GST&query=SR(10,20)
        //M51
        //http://imgsrc.hubblesite.org/hu/db/2001/10/images/a/formats/web_print.jpg
        //http://www.seds.org/messier/Jpg/m51.jpg
        //http://www.noao.edu/outreach/aop/observers/m51block4.jpg
        //http://www.noao.edu/image_gallery/images/d5/m51a.jpg
        //http://ipac.jpl.nasa.gov/media_images/ssc2004-19a_medium.jpg
    }

    private void updateImage(String key) {
        String address;
        try {
            address = images.get(key).toString();
        } catch (Exception e) {
            return;
        }
        imageURL.setText(address);
        if (address != null && address.length() != 0) {
            (new GetImageThread(imagePane.getViewport(), imageList, address)).start();
        }
    }


    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        mainPanel = new javax.swing.JPanel();
        jSplitPane1 = new javax.swing.JSplitPane();
        jPanel1 = new javax.swing.JPanel();
        jLabel6 = new javax.swing.JLabel();
        jScrollPane3 = new javax.swing.JScrollPane();
        namesArea = new javax.swing.JTextArea();
        commentPanel = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextArea1 = new javax.swing.JTextArea();
        jLabel7 = new javax.swing.JLabel();
        jTextField1 = new javax.swing.JTextField();
        commentExPanel = new ExpandPanel("Comments");
        propPanel = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        latitudeField = new javax.swing.JFormattedTextField();
        longitudeField = new javax.swing.JFormattedTextField();
        dateField = new javax.swing.JFormattedTextField();
        propExPanel = new ExpandPanel("Properties");
        jScrollPane2 = new javax.swing.JScrollPane();
        jXTable1 = new org.jdesktop.swingx.JXTable();
        jTabbedPane1 = new javax.swing.JTabbedPane();
        //This is a retarded hack to work around setting 'Layout'
        // on SearchPanel
        javax.swing.JPanel tmpSearchPanel = new SearchPanel();
        searchPanel = new javax.swing.JPanel();
        searchPanel = new javax.swing.JPanel();
        jPanel9 = new javax.swing.JPanel();
        imageList = new javax.swing.JComboBox();
        imageURL = new javax.swing.JTextField();
        imagePane = new javax.swing.JScrollPane();
        menuMainPanel = new javax.swing.JPanel();
        menuPanel = new javax.swing.JPanel();
        jLabel5 = new javax.swing.JLabel();
        siderealTimeField = new javax.swing.JFormattedTextField();
        localTimeField = new javax.swing.JFormattedTextField();
        jLabel4 = new javax.swing.JLabel();
        menuBar = new javax.swing.JMenuBar();
        javax.swing.JMenu fileMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem exitMenuItem = new javax.swing.JMenuItem();
        javax.swing.JMenu helpMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem aboutMenuItem = new javax.swing.JMenuItem();
        statusPanel = new javax.swing.JPanel();
        javax.swing.JSeparator statusPanelSeparator = new javax.swing.JSeparator();
        statusMessageLabel = new javax.swing.JLabel();
        statusAnimationLabel = new javax.swing.JLabel();
        progressBar = new javax.swing.JProgressBar();
        buttonGroup1 = new javax.swing.ButtonGroup();

        mainPanel.setName("mainPanel"); // NOI18N

        jSplitPane1.setName("jSplitPane1"); // NOI18N

        jPanel1.setName("jPanel1"); // NOI18N
        jPanel1.setPreferredSize(new java.awt.Dimension(200, 647));

        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance().getContext().getResourceMap(DSOTonightView.class);
        jLabel6.setText(resourceMap.getString("jLabel6.text")); // NOI18N
        jLabel6.setName("jLabel6"); // NOI18N

        jScrollPane3.setName("jScrollPane3"); // NOI18N

        namesArea.setColumns(20);
        namesArea.setEditable(false);
        namesArea.setRows(5);
        namesArea.setName("namesArea"); // NOI18N
        jScrollPane3.setViewportView(namesArea);

        commentPanel.setName("commentPanel"); // NOI18N

        jScrollPane1.setName("jScrollPane1"); // NOI18N

        jTextArea1.setColumns(20);
        jTextArea1.setRows(5);
        jTextArea1.setName("jTextArea1"); // NOI18N
        jScrollPane1.setViewportView(jTextArea1);

        jLabel7.setText(resourceMap.getString("jLabel7.text")); // NOI18N
        jLabel7.setName("jLabel7"); // NOI18N

        jTextField1.setText(resourceMap.getString("jTextField1.text")); // NOI18N
        jTextField1.setName("jTextField1"); // NOI18N

        org.jdesktop.layout.GroupLayout commentPanelLayout = new org.jdesktop.layout.GroupLayout(commentPanel);
        commentPanel.setLayout(commentPanelLayout);
        commentPanelLayout.setHorizontalGroup(
            commentPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(commentPanelLayout.createSequentialGroup()
                .add(2, 2, 2)
                .add(jLabel7)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jTextField1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 152, Short.MAX_VALUE))
            .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 180, Short.MAX_VALUE)
        );
        commentPanelLayout.setVerticalGroup(
            commentPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, commentPanelLayout.createSequentialGroup()
                .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 98, Short.MAX_VALUE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(commentPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jTextField1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jLabel7)))
        );

        commentExPanel.setName("commentExPanel"); // NOI18N

        org.jdesktop.layout.GroupLayout commentExPanelLayout = new org.jdesktop.layout.GroupLayout(commentExPanel);
        commentExPanel.setLayout(commentExPanelLayout);
        commentExPanelLayout.setHorizontalGroup(
            commentExPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 180, Short.MAX_VALUE)
        );
        commentExPanelLayout.setVerticalGroup(
            commentExPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 20, Short.MAX_VALUE)
        );

        propPanel.setName("propPanel"); // NOI18N

        jLabel1.setText(resourceMap.getString("jLabel1.text")); // NOI18N
        jLabel1.setName("jLabel1"); // NOI18N

        jLabel2.setText(resourceMap.getString("jLabel2.text")); // NOI18N
        jLabel2.setName("jLabel2"); // NOI18N

        jLabel3.setText(resourceMap.getString("jLabel3.text")); // NOI18N
        jLabel3.setName("jLabel3"); // NOI18N

        latitudeField.setText(resourceMap.getString("latitudeField.text")); // NOI18N
        latitudeField.setName("latitudeField"); // NOI18N
        latitudeField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                latitudeFieldActionPerformed(evt);
            }
        });
        latitudeField.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                latitudeFieldFocusLost(evt);
            }
        });

        longitudeField.setText(resourceMap.getString("longitudeField.text")); // NOI18N
        longitudeField.setName("longitudeField"); // NOI18N
        longitudeField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                longitudeFieldActionPerformed(evt);
            }
        });
        longitudeField.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                longitudeFieldFocusLost(evt);
            }
        });

        dateField.setText(resourceMap.getString("dateField.text")); // NOI18N
        dateField.setName("dateField"); // NOI18N

        org.jdesktop.layout.GroupLayout propPanelLayout = new org.jdesktop.layout.GroupLayout(propPanel);
        propPanel.setLayout(propPanelLayout);
        propPanelLayout.setHorizontalGroup(
            propPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(propPanelLayout.createSequentialGroup()
                .addContainerGap()
                .add(propPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jLabel3)
                    .add(jLabel2)
                    .add(jLabel1))
                .add(19, 19, 19)
                .add(propPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
                    .add(org.jdesktop.layout.GroupLayout.LEADING, longitudeField)
                    .add(org.jdesktop.layout.GroupLayout.LEADING, propPanelLayout.createSequentialGroup()
                        .add(1, 1, 1)
                        .add(dateField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 93, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                    .add(latitudeField))
                .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        propPanelLayout.setVerticalGroup(
            propPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, propPanelLayout.createSequentialGroup()
                .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .add(propPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(dateField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jLabel1))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(propPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(longitudeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 20, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jLabel2))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(propPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jLabel3)
                    .add(latitudeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );

        propExPanel.setName("propExPanel"); // NOI18N

        org.jdesktop.layout.GroupLayout propExPanelLayout = new org.jdesktop.layout.GroupLayout(propExPanel);
        propExPanel.setLayout(propExPanelLayout);
        propExPanelLayout.setHorizontalGroup(
            propExPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 180, Short.MAX_VALUE)
        );
        propExPanelLayout.setVerticalGroup(
            propExPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 20, Short.MAX_VALUE)
        );

        jScrollPane2.setName("jScrollPane2"); // NOI18N

        SkyDBTableModel model = new SkyDBTableModel();
        jXTable1.setModel(model);
        model.createColumns();
        jXTable1.setColumnSelectionAllowed(true);
        jXTable1.setEditable(false);
        jXTable1.setName("jXTable1"); // NOI18N
        jScrollPane2.setViewportView(jXTable1);
        jXTable1.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jXTable1.setColumnControlVisible(true);

        org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, jScrollPane3, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 180, Short.MAX_VALUE)
                    .add(commentPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .add(jLabel6)
                    .add(commentExPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .add(propExPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .add(propPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .add(jScrollPane2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 180, Short.MAX_VALUE))
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel1Layout.createSequentialGroup()
                .add(propExPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(propPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jScrollPane2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 120, Short.MAX_VALUE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jLabel6)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jScrollPane3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 141, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(commentExPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .add(1, 1, 1)
                .add(commentPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        jSplitPane1.setLeftComponent(jPanel1);

        jTabbedPane1.setName("jTabbedPane1"); // NOI18N

        searchPanel.setName("searchPanel"); // NOI18N

        org.jdesktop.layout.GroupLayout searchPanelLayout = new org.jdesktop.layout.GroupLayout(searchPanel);
        searchPanel.setLayout(searchPanelLayout);
        searchPanelLayout.setHorizontalGroup(
            searchPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 806, Short.MAX_VALUE)
        );
        searchPanelLayout.setVerticalGroup(
            searchPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 547, Short.MAX_VALUE)
        );

        searchPanel = tmpSearchPanel;

        jTabbedPane1.addTab(resourceMap.getString("searchPanel.TabConstraints.tabTitle"), searchPanel); // NOI18N

        jPanel9.setName("jPanel9"); // NOI18N

        imageList.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        imageList.setName("imageList"); // NOI18N
        imageList.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                imageListActionPerformed(evt);
            }
        });

        imageURL.setEditable(false);
        imageURL.setText(resourceMap.getString("imageURL.text")); // NOI18N
        imageURL.setName("imageURL"); // NOI18N

        imagePane.setName("imagePane"); // NOI18N

        org.jdesktop.layout.GroupLayout jPanel9Layout = new org.jdesktop.layout.GroupLayout(jPanel9);
        jPanel9.setLayout(jPanel9Layout);
        jPanel9Layout.setHorizontalGroup(
            jPanel9Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(imagePane, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 806, Short.MAX_VALUE)
            .add(jPanel9Layout.createSequentialGroup()
                .add(imageList, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 135, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .add(18, 18, 18)
                .add(imageURL, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 643, Short.MAX_VALUE)
                .addContainerGap())
        );
        jPanel9Layout.setVerticalGroup(
            jPanel9Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel9Layout.createSequentialGroup()
                .add(jPanel9Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(imageList, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(imageURL, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(imagePane, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 521, Short.MAX_VALUE))
        );

        jTabbedPane1.addTab(resourceMap.getString("jPanel9.TabConstraints.tabTitle"), jPanel9); // NOI18N

        jSplitPane1.setRightComponent(jTabbedPane1);

        menuMainPanel.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
        menuMainPanel.setName("menuMainPanel"); // NOI18N
        menuMainPanel.setPreferredSize(new java.awt.Dimension(120, 30));

        menuPanel.setName("menuPanel"); // NOI18N

        org.jdesktop.layout.GroupLayout menuPanelLayout = new org.jdesktop.layout.GroupLayout(menuPanel);
        menuPanel.setLayout(menuPanelLayout);
        menuPanelLayout.setHorizontalGroup(
            menuPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 379, Short.MAX_VALUE)
        );
        menuPanelLayout.setVerticalGroup(
            menuPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(0, 19, Short.MAX_VALUE)
        );

        menuPanel.setLayout(new java.awt.BorderLayout());
        menuPanel.add(menuBar,java.awt.BorderLayout.EAST);

        jLabel5.setText(resourceMap.getString("jLabel5.text")); // NOI18N
        jLabel5.setName("jLabel5"); // NOI18N

        siderealTimeField.setEditable(false);
        siderealTimeField.setText(resourceMap.getString("siderealTimeField.text")); // NOI18N
        siderealTimeField.setName("siderealTimeField"); // NOI18N

        localTimeField.setEditable(false);
        localTimeField.setText(resourceMap.getString("localTimeField.text")); // NOI18N
        localTimeField.setName("localTimeField"); // NOI18N

        jLabel4.setText(resourceMap.getString("jLabel4.text")); // NOI18N
        jLabel4.setName("jLabel4"); // NOI18N

        org.jdesktop.layout.GroupLayout menuMainPanelLayout = new org.jdesktop.layout.GroupLayout(menuMainPanel);
        menuMainPanel.setLayout(menuMainPanelLayout);
        menuMainPanelLayout.setHorizontalGroup(
            menuMainPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, menuMainPanelLayout.createSequentialGroup()
                .add(menuPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 343, Short.MAX_VALUE)
                .add(jLabel4)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
                .add(localTimeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .add(24, 24, 24)
                .add(jLabel5)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(siderealTimeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 72, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );
        menuMainPanelLayout.setVerticalGroup(
            menuMainPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(menuMainPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                .add(menuMainPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(siderealTimeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jLabel5))
                .add(org.jdesktop.layout.GroupLayout.TRAILING, menuMainPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jLabel4)
                    .add(localTimeField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
            .add(menuPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
        );

        org.jdesktop.layout.GroupLayout mainPanelLayout = new org.jdesktop.layout.GroupLayout(mainPanel);
        mainPanel.setLayout(mainPanelLayout);
        mainPanelLayout.setHorizontalGroup(
            mainPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(mainPanelLayout.createSequentialGroup()
                .addContainerGap()
                .add(jSplitPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 1018, Short.MAX_VALUE)
                .addContainerGap())
            .add(menuMainPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 1038, Short.MAX_VALUE)
        );
        mainPanelLayout.setVerticalGroup(
            mainPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(mainPanelLayout.createSequentialGroup()
                .add(menuMainPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 24, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
                .add(jSplitPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 577, Short.MAX_VALUE))
        );

        menuBar.setName("menuBar"); // NOI18N

        fileMenu.setText(resourceMap.getString("fileMenu.text")); // NOI18N
        fileMenu.setName("fileMenu"); // NOI18N

        exitMenuItem.setName("exitMenuItem"); // NOI18N
        fileMenu.add(exitMenuItem);

        menuBar.add(fileMenu);

        helpMenu.setText(resourceMap.getString("helpMenu.text")); // NOI18N
        helpMenu.setName("helpMenu"); // NOI18N

        aboutMenuItem.setName("aboutMenuItem"); // NOI18N
        helpMenu.add(aboutMenuItem);

        menuBar.add(helpMenu);

        statusPanel.setName("statusPanel"); // NOI18N

        statusPanelSeparator.setName("statusPanelSeparator"); // NOI18N

        statusMessageLabel.setName("statusMessageLabel"); // NOI18N

        statusAnimationLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        statusAnimationLabel.setName("statusAnimationLabel"); // NOI18N

        progressBar.setName("progressBar"); // NOI18N

        org.jdesktop.layout.GroupLayout statusPanelLayout = new org.jdesktop.layout.GroupLayout(statusPanel);
        statusPanel.setLayout(statusPanelLayout);
        statusPanelLayout.setHorizontalGroup(
            statusPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(statusPanelLayout.createSequentialGroup()
                .addContainerGap()
                .add(statusMessageLabel)
                .add(245, 245, 245)
                .add(statusPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, statusPanelLayout.createSequentialGroup()
                        .add(375, 375, 375)
                        .add(progressBar, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(statusAnimationLabel)
                        .addContainerGap())
                    .add(statusPanelLayout.createSequentialGroup()
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(statusPanelSeparator, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 783, Short.MAX_VALUE))))
        );
        statusPanelLayout.setVerticalGroup(
            statusPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(statusPanelLayout.createSequentialGroup()
                .add(statusPanelSeparator, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 2, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .add(statusPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(statusMessageLabel)
                    .add(statusAnimationLabel)
                    .add(progressBar, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .add(3, 3, 3))
        );

        setComponent(mainPanel);
        setStatusBar(statusPanel);
    }// </editor-fold>//GEN-END:initComponents

    private void imageListActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_imageListActionPerformed
        // TODO add your handling code here:
        if (imageList.getSelectedItem() == null) {
            return;
        }
        String key = imageList.getSelectedItem().toString();
        updateImage(key);

    }//GEN-LAST:event_imageListActionPerformed

    private void longitudeFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_longitudeFieldActionPerformed
        Double longitude = SiderealDate.stringToLongitude(longitudeField.getText());
        if (longitude.isNaN()) {
                longitudeField.setBackground(Color.red);
            } else {
                longitudeField.setBackground(Color.white);
                lonW = longitude;
                longitudeField.setText(SiderealDate.DeclinationToString(lonW));
                Preferences prefs = Preferences.userNodeForPackage(dsotonight.DSOTonightApp.class);
                prefs.put("Longitude", Double.toString(lonW));
                ((SearchPanel)searchPanel).setLonLat(lonW, latN);
            }
    }//GEN-LAST:event_longitudeFieldActionPerformed

    private void latitudeFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_latitudeFieldActionPerformed
        Double latitude = SiderealDate.stringToDeclination(latitudeField.getText());
        if (latitude.isNaN()) {
                latitudeField.setBackground(Color.red);
            } else {
                latitudeField.setBackground(Color.white);
                latN = latitude;
                latitudeField.setText(SiderealDate.DeclinationToString(latN));
                Preferences prefs = Preferences.userNodeForPackage(dsotonight.DSOTonightApp.class);
                prefs.put("Latitude", Double.toString(latN));
                ((SearchPanel)searchPanel).setLonLat(lonW, latN);
            }
    }//GEN-LAST:event_latitudeFieldActionPerformed

    private void longitudeFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_longitudeFieldFocusLost
        longitudeFieldActionPerformed(null);
    }//GEN-LAST:event_longitudeFieldFocusLost

    private void latitudeFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_latitudeFieldFocusLost
        latitudeFieldActionPerformed(null);
    }//GEN-LAST:event_latitudeFieldFocusLost

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.JPanel commentExPanel;
    private javax.swing.JPanel commentPanel;
    private javax.swing.JFormattedTextField dateField;
    private javax.swing.JComboBox imageList;
    private javax.swing.JScrollPane imagePane;
    private javax.swing.JTextField imageURL;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel9;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JSplitPane jSplitPane1;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JTextArea jTextArea1;
    private javax.swing.JTextField jTextField1;
    private org.jdesktop.swingx.JXTable jXTable1;
    private javax.swing.JFormattedTextField latitudeField;
    private javax.swing.JFormattedTextField localTimeField;
    private javax.swing.JFormattedTextField longitudeField;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JPanel menuMainPanel;
    private javax.swing.JPanel menuPanel;
    private javax.swing.JTextArea namesArea;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JPanel propExPanel;
    private javax.swing.JPanel propPanel;
    private javax.swing.JPanel searchPanel;
    private javax.swing.JFormattedTextField siderealTimeField;
    private javax.swing.JLabel statusAnimationLabel;
    private javax.swing.JLabel statusMessageLabel;
    private javax.swing.JPanel statusPanel;
    // End of variables declaration//GEN-END:variables
    private final Timer messageTimer;
    private final Timer clockRefresh;
    DateFormat timeFormat;
    private final Timer busyIconTimer;
    private final Icon idleIcon;
    private final Icon[] busyIcons = new Icon[15];
    private int busyIconIndex = 0;
    private JDialog aboutBox;
    private SkyDBImage image;
    private Hashtable images;
    double lonW;
    double latN;

}
class GetImageThread extends Thread {

    JViewport viewport;
    javax.swing.JComboBox combo;
    String address;

    GetImageThread(JViewport _viewport, javax.swing.JComboBox _combo, String _address) {
        viewport = _viewport;
        combo = _combo;
        address = _address;
    }

    public void run() {
        combo.setEnabled(false);
        try {
            URL url = new URL(address);
            BufferedImage img = ImageIO.read(url);
            ImageComponent im1 = new ImageComponent(img);
            viewport.add(im1);
        } catch (Exception e) {
            System.out.println("Failed to read image: " + address);
        }
        combo.setEnabled(true);
    }
}
